﻿using MCUPackage;
using MCUPackage.Utils;
using Newtonsoft.Json;
using System;
using System.ComponentModel;

namespace SxPackages.STC32G.Models
{
	/// <summary>
	/// 定时器选项
	/// </summary>
	[Serializable]
	class TmrOptions
	{
		protected STC32GQFP64 mParent = null;

		/// <summary>
		/// 定时器ID
		/// </summary>
		[Browsable(false)]
		public int Id { get; private set; }

		private bool mEnable = false;
		[DisplayName("使能"),
		DefaultValue(false),
		JsonProperty(Order = 100),
		Description("是否使能本定时器/计数器")]
		public bool Enable
		{
			get => mEnable;
			set
			{
				if (value != mEnable)
				{
					if (value)
					{
						if (mParent.Uarts != null)
						{
							var uarts = mParent.mUarts.All;
							foreach (var uart in uarts)
							{
								if ((int)uart.Timer == Id && uart.Enable)
								{
									GuiUtils.MsgBox($"本定时器已被串口{uart.Id}用作波特率发生器!");
									return;
								}
							}
						}
					}
					mEnable = value;
				}
			}
		}

		private TmrMode mMode = TmrMode.自动重载16位;
		[DisplayName("工作模式"),
		DefaultValue(TmrMode.自动重载16位),
		Description("定时器工作模式设置")]
		public TmrMode Mode
		{
			get => mMode;
			set
			{
				if (value != mMode)
				{
					mMode = value;
					UpdatePeriodRange();
				}
			}
		}

		private bool mEnable12T = false;
		[DisplayName("时钟源设置"),
		DefaultValue(false),
		Description("定时器是否使用系统时钟的12分频时钟源, 否则直接使用系统时钟源")]
		public bool Enable12T
		{
			get => mEnable12T;
			set
			{
				if (value != mEnable12T)
				{
					mEnable12T = value;
					UpdatePeriodRange();
				}
			}
		}

		private byte mPS = 0;
		[DisplayName("分频设置"),
		DefaultValue(0),
		Description("对时钟源进行分频后使用")]
		public byte PS
		{
			get => mPS;
			set
			{
				if (value != mPS)
				{
					mPS = value;
					UpdatePeriodRange();
				}
			}
		}

		private int mPeriod = 1000;
		[DisplayName("中断周期"),
		DefaultValue(1000),
		Description("定时中断的周期数, 单位us")]
		public int Period
		{
			get => mPeriod;
			set
			{
				if (value != mPeriod)
				{
					if (value < mPeriodMin || value > mPeriodMax)
					{
						GuiUtils.MsgBox("请输入有效周期数!");
						return;
					}
					mPeriod = value;
					//					GuiHelper.MsgBox(GetTmrString());
				}
			}
		}

		[DisplayName("中断优先级"),
		Description("中断优先级等级设置"),
		DefaultValue(PriorityTypes.最低级)]
		public PriorityTypes Priority { get; set; } = PriorityTypes.最低级;

		/// <summary>
		/// 根据参数值计算周期
		/// </summary>
		private int CalPeriod(UInt16 aValue)
		{
			UInt64 value = (UInt64)(Mode == TmrMode.自动重载8位 ? 256 : 65536);
			value -= aValue;
			// 计算到微秒, 本系统存的主频为KHz
			value *= 1000;
			value *= (UInt16)(PS + 1);
			if (Enable12T)
			{
				value *= 12;
			}

			return (int)(value / mParent.mSYSTEM.FreqValue);
		}

		/// <summary>
		/// 获取当前设置溢出寄存器值
		/// </summary>
		/// <returns></returns>
		public UInt16 GetTmrValue()
		{
			// 公式计算的是秒, 系统存储的是微秒
			double value = Period;

			// 此时计算的为毫秒
			value *= mParent.mSYSTEM.FreqValue;
			value /= (PS + 1);
			if (Enable12T)
			{
				value /= 12;
			}
			// 转为秒
			value /= 1000;

			if (Mode != TmrMode.自动重载8位 && value > 65535)
			{
				return 0;
			}
			if (Mode == TmrMode.自动重载8位 && value > 255)
			{
				return 0;
			}

			return (UInt16)((Mode == TmrMode.自动重载8位 ? 256 : 65536) - value);
		}

		/// <summary>
		/// 获取赋值的计算公式
		/// </summary>
		/// <returns></returns>
		public string GetTmrString()
		{
			decimal value = Period;
			decimal denominator = (PS + 1);

			if (Enable12T)
			{
				denominator *= 12;
			}

			// 转为秒
			denominator *= 1000000;

			Fractional num = Fractional.Simplify(value, denominator);

			string str = string.Empty;
			if (Mode == TmrMode.自动重载8位)
			{
				str = $"(uint8_t)(256 - (MAIN_Fosc ";
			}
			else
			{
				str = $"(uint16_t)(65536 - (MAIN_Fosc ";
			}
			if (num.Member == 1)
			{
				str += $"/ {num.Denominator}))";
			}
			else
			{
				str += $"* {num.Member}";
				if (num.Denominator > 1)
				{
					str += $" / {num.Denominator}";
				}
				str += "))";
			}

			return str;
		}

		/// <summary>
		/// 更新周期区域
		/// </summary>
		private void UpdatePeriodRange()
		{
			mPeriodMin = CalPeriod((UInt16)(Mode == TmrMode.自动重载8位 ? 255 : 65535));
			mPeriodMax = CalPeriod(0);

			if (Period < mPeriodMin)
			{
				Period = mPeriodMin;
			}
			else if (Period > mPeriodMax)
			{
				Period = mPeriodMax;
			}

			string str = string.Empty;
			if (mPeriodMin >= 10000)
			{
				str = $"{(double)mPeriodMin / 1000:F1}ms";
			}
			else
			{
				str = $"{mPeriodMin}us";
			}
			if (mPeriodMax >= 10000)
			{
				str += $"~{(double)mPeriodMax / 1000:F1}ms";
			}
			else
			{
				str += $"~{mPeriodMax}us";
			}
			mParent.UpdateMsg($"中断周期为{str}");
		}

		/// <summary>
		/// 中断周期的下限
		/// </summary>
		private int mPeriodMin = 0;
		/// <summary>
		/// 中断周期的上限
		/// </summary>
		private int mPeriodMax = 0;

		public TmrOptions(int aId, STC32GQFP64 aParent)
		{
			if (aId < 0 || (aId > 4 && aId != 11))
			{
				throw new Exception("定时器ID错误!");
			}
			this.Id = aId;
			this.mParent = aParent;
			mPeriodMin = CalPeriod((UInt16)(Mode == TmrMode.自动重载8位 ? 255 : 65535));
			mPeriodMax = CalPeriod(0);
			mPeriod = mPeriodMax < 1000 ? mPeriodMax : 1000;
		}

		public override String ToString()
		{
			return string.Empty;
		}

		/// <summary>
		/// 初始化模板
		/// </summary>
		private const string TEMPLATE =
@"	// 定时器{5}设置中断周期为{0}us
	attr.TIM_Mode      = {1};	//指定工作模式,   TIM_16BitAutoReload,TIM_16Bit,TIM_8BitAutoReload,TIM_16BitAutoReloadNoMask
	attr.TIM_ClkSource = {2};		//指定时钟源,     TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
	attr.TIM_ClkOut    = DISABLE;				//是否输出高速脉冲, ENABLE或DISABLE
	attr.TIM_Value     = {3};		//初值,
	attr.TIM_PS        = {4};					//8位预分频器(n+1), 0~255
	attr.TIM_Run       = ENABLE;				//是否初始化后启动定时器, ENABLE或DISABLE
	Timer_Inilize(Timer{5}, &attr);				//初始化Timer0	  Timer0,Timer1,Timer2,Timer3,Timer4
	NVIC_Timer{5}_Init(ENABLE, {6});		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3";

		public string Generate()
		{
			if (!this.Enable)
			{
				return null;
			}

			return string.Format(TEMPLATE, this.Period, this.Mode.ToDescription(), this.Enable12T ? "TIM_CLOCK_12T" : "TIM_CLOCK_1T",
								GetTmrString(), this.PS, this.Id, this.Priority.ToDescription());
		}
	}
}
